package com.brave.base;

import com.alibaba.fastjson.JSONObject;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * PackageName:com.brave.repository
 * CreateTime:2019/7/10 -16:31
 * Author:BraveKing
 * Params:
 **/
public class CommonRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CommonRepository<T, ID>
{
    
    //默认查询条件
    private static final int defaultPageSize = 10;
    private static final int defaultPageNumber = 1;
    
    private final EntityManager entityManager;
    private final Class<T> cls;
    
    //Default Constructor
    public CommonRepositoryImpl(JpaEntityInformation information, EntityManager entityManager)
    {
        super(information, entityManager);
        this.entityManager = entityManager;
        this.cls = getDomainClass();
    }


    /**
     * 2019年7月11日 08点37分
     * 动态SQL查询, 可以设置缓存
     * @param sql 拼装完成后可以执行查询的SQL
     * @param pageSize 查询每页记录数量
     * @param pageNumber 当前页码
     * @return
     */
    @Override
    public Map<String, Object> findPageList(String sql, int pageSize, int pageNumber)
    {
        pageSize = pageSize == 0 ? defaultPageSize : pageSize;
        pageNumber = pageNumber == 0 ? defaultPageNumber : pageNumber;
        //Assert Param Check
        Assert.notNull(sql, "Query SQL can not be NULL, please check your code .");
        Assert.notNull(pageSize, "Pageable param 'pageSize' can not be NULL or 0, please check your code .");
        Assert.notNull(pageNumber, "Pageable param 'pageNumber' can not be NULL or 0, please check your code .");
        //Return Result
        Map<String, Object> resultMap = new HashMap<>();
        Query query = entityManager.createNativeQuery(sql);
        long count = query.getResultList().size();
        //Check Result Lines weather it is null or 0
        if(0 == count)
        {
            resultMap.put("rows", new ArrayList<Map<String, Object>>());
            resultMap.put("totalCount", 0);
            resultMap.put("totalPage", 0);
            resultMap.put("pageNumber", pageNumber);
            resultMap.put("pageSize", pageSize);
        }else
        {
            List<Map<String, Object>> list = query.setFirstResult((pageNumber - 1) * pageSize).setMaxResults(pageSize)
                    .unwrap(NativeQueryImpl.class)
                    .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
                    .getResultList();
            resultMap.put("rows", list);
            resultMap.put("totalCount", count);
            if(count % pageSize == 0)
            {
                resultMap.put("totalPage", count / pageSize);
            }else
            {
                resultMap.put("totalPage", count / pageSize + 1);
            }
            resultMap.put("pageNumber", pageNumber);
            resultMap.put("pageSize", pageSize);
        }
        return resultMap;
    }


    /**
     * 2019年7月11日 08点52分
     * 动态SQL查询-不带分页功能
     * @param sql 拼装好执行查询的SQL
     * @return
     */
    @Override
    public <OT> List<OT> findList(String sql)
    {
        Assert.notNull(sql, "Query SQL can not be NULL, please check your code .");
        Query query = entityManager.createNativeQuery(sql);
        List<OT> list = query.unwrap(NativeQueryImpl.class)
                .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
                .getResultList();
        if(null != list && 0 < list.size())
        {
            return list;
        }
        return new ArrayList<>(2);
    }


    /**
     * 2019年7月17日 16点54分
     * 查询数组形式数据
     * @param sql
     * @return
     */
    @Override
    public List<Object> findArrays(String sql)
    {
        return entityManager.createNativeQuery(sql).getResultList();
    }
    
    
    /**
     * 2019年7月11日 08点54分
     * 动态SQL查询, 分页且带缓存
     * @param sql 拼装完成后可以执行查询的SQL
     * @param pageSize 查询每页记录数量
     * @param pageNumber 当前页码
     * @return
     */
    @Override
    @Cacheable(cacheNames = "findCachePageList")
    public Map<String, Object> findCachePageList(String sql, int pageSize, int pageNumber)
    {
        return this.findPageList(sql, pageSize, pageNumber);
    }


    /**
     * 2019年7月11日 08点53分
     * 动态SQL查询-不带分页功能
     * @param sql 拼装好执行查询的SQL
     * @return
     */
    @Override
    @Cacheable(cacheNames = "findCacheList")
    public <OT> List<OT> findCacheList(String sql)
    {
        return this.findList(sql);
    }

    
    @Override
    public <BID, OT> int deleteEntityById(BID id, Class<OT> t)
    {
        try
        {
            OT o = entityManager.find(t, id);
            if(null != o)
            {
                return entityManager.createQuery("DELETE FROM " + t.getSimpleName() + " obj WHERE obj.id = '" + id + "' ").executeUpdate();
            }
            return 0;
        }catch(Exception ex)
        {
            return 0;
        }
    }


    /**
     * 2019年7月11日 10点59分
     * 动态SQL查询-不带分页-转换实体
     * @param sql
     * @param entityClass
     * @param <OT>
     * @return
     */
    @Override
    public <OT> List<OT> findEntityList(String sql, Class<OT> entityClass)
    {
        Assert.notNull(sql, "Query SQL can not be NULL, please check your code .");
        Query query = entityManager.createNativeQuery(sql);
        List<Map<String, Object>> list = query.unwrap(NativeQueryImpl.class)
                .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
                .getResultList();
        List<OT> entityResultList = new ArrayList<>();
        list.stream().forEach(map -> 
        {
            OT entity = JSONObject.parseObject(JSONObject.toJSONString(map), entityClass);
            entityResultList.add(entity);
        });
        return entityResultList;
    }
}